#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import random
import pygame

from UI import GameUI
from music import GameMusic
from base import Node, Point, Direction
from config import GameConfig

import tkinter as tk
import tkinter.messagebox
from PIL import ImageTk
import UserDB
import ScoresRank

class Food:
    def __init__(self, node: Node, point: Point):
        self.node = node
        self.point = point

    def to_snake_node(self):
        return SnakeNode(node=self.node, point=self.point)


class PoisonFood:
    def __init__(self, node: Node, point: Point):
        self.node = node
        self.point = point


class SnakeNode:
    def __init__(self, node: Node, point: Point):
        self.node = node
        self.point = point


class Snake:
    def __init__(self):
        self.head = None
        self.tail = None
        self.stomach = []
        self.body = []
        self.direction = Direction("right")

    def set_head(self, node: Node, point: Point):
        node.image = pygame.transform.rotate(node.image, -90)
        self.head = SnakeNode(node=node, point=point)

    def set_tail(self, node: Node, point: Point):
        self.tail = SnakeNode(node=node, point=point)

    def to_list(self):
        nodes = [self.head]
        nodes.extend(self.body)
        nodes.append(self.tail)
        return nodes

    def move(self, direction: Direction):
        if direction == self.direction or direction.opposite == self.direction:
            return True

        clockwise = [("up", "right"), ("left", "up"), ("down", "left"), ("right", "down")]

        if (self.direction, direction) in clockwise:
            turn_angle = -90
        else:
            turn_angle = 90

        self.head.node.image = pygame.transform.rotate(self.head.node.image, turn_angle)
        self.direction = direction
        return turn_angle

    def forward(self):
        self._forward_tail()
        self._forward_body()
        self._forward_head()

    def _forward_head(self):
        if self.direction == "left":
            self.head.point.px = self.head.point.px - 1
        elif self.direction == "right":
            self.head.point.px = self.head.point.px + 1
        elif self.direction == "up":
            self.head.point.py = self.head.point.py - 1
        elif self.direction == "down":
            self.head.point.py = self.head.point.py + 1

    def _forward_body(self):
        if self.stomach:  # 胃部有食物, 尾部不动, 食物追加到身体末尾
            food = self.stomach.pop()
            snake_node = food.to_snake_node()
            self.body.append(snake_node)

        if self.body:
            for i in range(len(self.body) - 1, 0, -1):
                self.body[i].point.px = self.body[i - 1].point.px
                self.body[i].point.py = self.body[i - 1].point.py
            self.body[0].point.px = self.head.point.px
            self.body[0].point.py = self.head.point.py

    def _forward_tail(self):
        if self.stomach:
            return None  # 胃部有食物尾部不动
        if self.body:
            self.tail.point.px = self.body[-1].point.px
            self.tail.point.py = self.body[-1].point.py
        else:
            self.tail.point.px = self.head.point.px
            self.tail.point.py = self.head.point.py

    def eat(self, food, addscore):
        GameMusic.sound_effect_play(GameConfig.score_sound)
        addscore()
        self.stomach.append(food)

    def poi(self, addscore, stop):
        GameMusic.sound_effect_play(GameConfig.score_sound)
        addscore(num=-1)
        if self.body:
            self.body.pop(-1)
        else:
            stop()


class GamePainter:
    # 画家
    @classmethod
    def draw_food(cls, surface, food: Food):
        cls.draw_node(surface=surface, node=food.node, point=food.point)

    @classmethod
    def draw_poison(cls, surface, poison_food: PoisonFood):
        for p in poison_food:
            cls.draw_node(surface=surface, node=p.node, point=p.point)

    @classmethod
    def draw_snake(cls, surface, snake: Snake):
        for snode in snake.to_list():
            cls.draw_node(surface=surface, node=snode.node, point=snode.point)

    @classmethod
    def draw_node(cls, surface, node, point):
        pygame.draw.rect(
            surface,
            GameConfig.node_border_color,
            (
                point.pixel_x - GameConfig.node_border_width,
                point.pixel_y - GameConfig.node_border_width,
                GameConfig.node_width + 2 * GameConfig.node_border_width,
                GameConfig.node_height + 2 * GameConfig.node_border_width
            ),
            GameConfig.node_border_width,
            border_radius=GameConfig.node_border_radius
        )
        surface.blit(node.image, (point.pixel_x, point.pixel_y))


class GameJudge:
    # 裁判员
    @classmethod
    def is_collide_food(cls, head, food):
        # 是否碰撞到食物
        if head.point.px == food.point.px and head.point.py == food.point.py:
            return True
        else:
            return False

    @classmethod
    def is_collide_poison_food(cls, head, poison_food):
        # 是否碰撞到有毒
        if not poison_food:
            return -1
        for index, p in enumerate(poison_food):
            if head.point.px == p.point.px and head.point.py == p.point.py:
                return index
            else:
                # return -1
                continue
        return -1

    @classmethod
    def is_collide_self(cls, snake):
        # 和食物碰撞检测是相同的逻辑, 食物即是身体部分
        for node in snake.to_list()[1:]:
            if cls.is_collide_food(snake.head, node):
                return True
        return False

    @classmethod
    def is_collide_edge(cls, head):
        # 是否碰撞到墙壁
        is_collide = True
        if 0 <= head.point.pixel_x + GameConfig.node_width / 2 <= GameConfig.win_width \
                and 0 <= head.point.pixel_y + GameConfig.node_height / 2 <= GameConfig.win_height:
            is_collide = False
        return is_collide

    @classmethod
    def is_next_level(cls, level, score):
        if score >= level * 10:
            return True
        else:
            return False


class Game:
    def __init__(self):
        self.display_surf = self.init_window()
        self.level = 1
        self.score = 0
        self.snake = None
        self.food = None
        self.poison_food = []
        self.running = True
        self.pause = False
        self.table_cols = GameConfig.win_width // GameConfig.node_width - 1
        self.table_rows = GameConfig.win_height // GameConfig.node_height - 1
        self.points = set([Point(px, py) for px in range(self.table_cols + 1) for py in range(self.table_rows + 1)])
        self.food_images = self.get_food_imgs()
        self.user_id=""

    def get_food_imgs(self):
        image_names = os.listdir(GameConfig.food_imgs)
        image_paths = [os.path.join(GameConfig.food_imgs, name) for name in image_names]
        if GameConfig.is_order_food:
            image_paths.sort()
        else:
            random.shuffle(image_paths)
        return image_paths

    @staticmethod
    def init_window():
        pygame.init()
        display_surf = pygame.display.set_mode(
            (GameConfig.win_width, GameConfig.win_height), 0, 32
        )
        display_surf.fill(GameConfig.win_bg_color)  # 背景

        logo_surf = pygame.image.load(GameConfig.game_logo).convert_alpha()
        pygame.display.set_icon(logo_surf)
        pygame.display.set_caption(GameConfig.game_name)
        return display_surf

    def init_snake(self):
        # 左上方开始
        snake = Snake()
        head_node = Node(img_path=GameConfig.snake_head_img)
        tail_node = Node(img_path=GameConfig.snake_tail_img)
        head_point = Point(1, 0)
        tail_point = Point(0, 0)
        snake.set_head(node=head_node, point=head_point)
        snake.set_tail(node=tail_node, point=tail_point)
        self.snake = snake

    def init_first_food(self):
        food_img = Node(img_path=GameConfig.first_food_img)
        point = self.find_point()
        food = Food(node=food_img, point=point)
        self.food = food

    def init_first_poison_food(self):
        self.poison_food = []
        poison_food_img = Node(img_path=GameConfig.poison_food)
        for i in range(self.level):
            point = self.find_point()
            poison_food = PoisonFood(node=poison_food_img, point=point)
            self.poison_food.append(poison_food)

    def reset_food(self):
        if self.food_images:
            food_img_path = self.food_images.pop()
            food_img = Node(img_path=food_img_path)
        else:
            self.food_images = self.get_food_imgs()  # 重新吃所有图片
            food_img_path = self.food_images.pop()
            food_img = Node(img_path=food_img_path)
        point = self.find_point()
        food = Food(node=food_img, point=point)
        self.food = food

    def reset_poison_food(self, index):
        self.poison_food.pop(index)

    def find_point(self):
        snake_nodes = self.snake.to_list() if self.snake else []
        snake_points = set([node.point for node in snake_nodes])
        empty_points = self.points - snake_points
        rand_point = random.choice(list(empty_points))
        return rand_point

    def run(self):
        GameMusic.bg_music_play(GameConfig.bgm)
        while True:
            GameUI.main_menu(self.display_surf, self.start, self.user,self.scoreRank)

    def scoreRank(self):
        
        
        s=ScoresRank.ScoreListWindow()
        
        s.showList()

    def user(self):
        # 用户登录/注册模块
        window = tk.Tk()
        window.title("用户登录/注册")
        window.geometry("600x500")
        # 背景画布
        canvas = tk.Canvas(window, width=600, height=500)
        image_file = ImageTk.PhotoImage(file="imgs/menu/init_bgimg.jpeg")
        image = canvas.create_image(0, 0, anchor="nw", image=image_file)
        canvas.pack()
        # 用户名，用户密码文本
        name_lable = tk.Label(window, text="UserName:", font=(18), bg='grey', fg='black')
        password_lable = tk.Label(window, text="Password:", font=(18), bg='grey', fg='black')
        name_lable.place(x=200, y=200)
        password_lable.place(x=200, y=240)
        # 用户名，用户密码输入框
        nameval = tk.StringVar()
        passwordval = tk.StringVar()
        name_entry = tk.Entry(window, textvariable=nameval, font=(18))
        password_entry = tk.Entry(window, textvariable=passwordval, show="*", font=(18))
        name_entry.place(x=280, y=200)
        password_entry.place(x=280, y=240)

        def sign_in_f():
            UR = UserDB.UserRegister()
            db = UR.ConnectSQL()
            user_id = name_entry.get()
            user_pw = password_entry.get()
            findResult = UR.Loginin(db, user_id, user_pw)
            if findResult == "Successfully Login!":
                self.user_id=user_id
                tk.messagebox.showinfo(title="登录提示", message="登录成功")
            elif findResult == "Wrong Password!":
                tk.messagebox.showinfo(title="登录提示", message="密码错误")
            else:
                tk.messagebox.showinfo(title="登录提示", message="该账号不存在")

        def sign_up_f():
            # 用户注册页面
            sign_up_w = tk.Tk()
            sign_up_w.title("用户注册")
            sign_up_w.geometry("400x300")

            s_up_IDLable = tk.Label(sign_up_w, text="UserID:", font=(18))
            s_up_PassLable = tk.Label(sign_up_w, text="Password:", font=(18))
            s_up_IDLable.place(x=120, y=100)
            s_up_PassLable.place(x=120, y=140)

            s_up_IDVal = tk.StringVar()
            s_up_PassVal = tk.StringVar()
            s_name_entry = tk.Entry(sign_up_w, textvariable=s_up_IDVal, width=20, font=(18))
            s_pass_entry = tk.Entry(sign_up_w, textvariable=s_up_PassVal, width=20, show="*", font=(18))
            s_name_entry.place(x=180, y=100)
            s_pass_entry.place(x=180, y=140)

            # 用户在注册页面点击注册按钮触发的函数
            def su_conf_b():
                su_ID = s_name_entry.get()
                su_Pass = s_pass_entry.get()
                UR = UserDB.UserRegister()
                db = UR.ConnectSQL()
                RegisterResult = UR.Register(db, su_ID, su_Pass)
                if RegisterResult:
                    tk.messagebox.showinfo(title="注册提示", message="注册成功，请登录")
                    sign_up_w.destroy()
                else:
                    tk.messagebox.showinfo(title="注册提示", message="该用户名已被注册!")

            su_confirm_button = tk.Button(sign_up_w, text="注册", command=su_conf_b)
            su_confirm_button.place(x=240, y=180)

        # 登录按钮触发函数
        sign_in_button = tk.Button(window, text="登录", command=sign_in_f)
        sign_up_button = tk.Button(window, text="注册", command=sign_up_f)
        sign_in_button.place(x=350, y=300)
        sign_up_button.place(x=480, y=300)

        window.mainloop()

    def start(self):
        if self.running:
            self.init_snake()
            self.init_first_food()
            self.init_first_poison_food()
            GamePainter.draw_snake(surface=self.display_surf, snake=self.snake)
            GamePainter.draw_food(surface=self.display_surf, food=self.food)
            GamePainter.draw_poison(surface=self.display_surf, poison_food=self.poison_food)
        clock = pygame.time.Clock()
        while self.running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.quit()
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_RIGHT:  # 不能倒着走
                        self.snake.move(Direction("right"))
                    elif event.key == pygame.K_LEFT:
                        self.snake.move(Direction("left"))
                    elif event.key == pygame.K_UP:
                        self.snake.move(Direction("up"))
                    elif event.key == pygame.K_DOWN:
                        self.snake.move(Direction("down"))
                    elif event.key == pygame.K_SPACE:
                        self.game_pause()
                        while self.pause:
                            GameUI.pause_menu(self.display_surf, self.game_continue)
            self.loop()
            GameUI.inform_menu(self.display_surf, self.score, self.level)
            speed = GameConfig.snake_move_speed
            key_pressed = pygame.key.get_pressed()
            key_list = [pygame.K_RIGHT, pygame.K_LEFT, pygame.K_UP, pygame.K_DOWN]
            for k in key_list:
                if key_pressed[k]:
                    speed = GameConfig.snake_move_speed * 1.5
                    break
                else:
                    speed = GameConfig.snake_move_speed
            clock.tick(speed * (1 + self.level * 0.15))  # 帧率, 也对于蛇的移动速度

    def restart(self):
        GameMusic.bg_music_play(GameConfig.bgm)
        self.init()
        self.running = True
        self.start()

    def loop(self):
        self.display_surf.fill(GameConfig.win_bg_color)
        self.snake.forward()
        GamePainter.draw_snake(surface=self.display_surf, snake=self.snake)
        GamePainter.draw_food(surface=self.display_surf, food=self.food)
        GamePainter.draw_poison(surface=self.display_surf, poison_food=self.poison_food)
        # pygame.display.update()
        if GameJudge.is_collide_self(self.snake) or GameJudge.is_collide_edge(self.snake.head):
            self.stop()
        if GameJudge.is_collide_food(self.snake.head, self.food):
            self.snake.eat(self.food, self.add_score)
            self.reset_food()
        index = GameJudge.is_collide_poison_food(self.snake.head, self.poison_food)
        if index >= 0:
            self.snake.poi(self.add_score, self.stop)
            self.reset_poison_food(index)
        if GameJudge.is_next_level(self.level, self.score):
            self.add_level()
            self.start()

    def stop(self):
        GameMusic.sound_effect_play(GameConfig.gameover_sound)
        self.running = False
        s=ScoresRank.ScoreListWindow()
        s.updateList(self.user_id,self.score)
        GameMusic.music_stop()
        while not self.running:
            GameUI.stop_menu(self.display_surf, self.restart, self.score)

    def game_continue(self):
        self.pause = False
        return self.pause

    def game_pause(self):
        self.pause = True
        return self.pause

    def add_score(self, num=1):
        self.score += num
        return self.score

    def add_level(self):
        self.level += 1
        return self.level

    def init(self):
        self.level = 1
        self.score = 0
        return self.score, self.level

    @staticmethod
    def quit():
        pygame.quit()


if __name__ == '__main__':
    game = Game()
    game.run()
